﻿using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
using Microsoft.Extensions.Configuration;
using MyTRCP.Common.MyProtocol;
using System.Net;
using InTheHand.Net;
using MyTRCP.Common.Models;

namespace MyTRCP.Common.Services
{
    public class MyProtocolClientSideService : BackgroundService
    {
        private readonly ILogger<MyProtocolClientSideService> _logger;
        private readonly ILogger<ClientSide> _csLogger;
        private IPEndPoint _ipep;
        private readonly AsymmetricCipherKeyPair _idKeyPair;
        private readonly List<ECPublicKeyParameters> _trustedBridgePublicKeys;
        private readonly AutoResetEvent _openEvent;
        private readonly AutoResetEvent _closeEvent;
        private readonly AutoResetEvent _openedEvent;
        private readonly AutoResetEvent _closedEvent;
        private readonly AutoResetEvent _exitEvent;
        private bool _exitFlag;
        private bool _openFlag;
        private ClientSide? _cs;
        private readonly BlockingQueue<byte[]> _audioRxQ;
        private readonly BlockingQueue<byte[]> _audioTxQ;
        private readonly BlockingQueue<byte[]> _ctrlRxQ;
        private readonly BlockingQueue<byte[]> _ctrlTxQ;
        public event EventHandler? DevAudioConnected;
        public event EventHandler? DevAudioDisconnected;
        public event EventHandler? DevCtrlConnected;
        public event EventHandler? DevCtrlDisconnected;
        public event EventHandler? SessionDroped;
        private bool _isAudioConnected;
        private bool _isCtrlConnected;
        private readonly bool _isRemoteMode;
        private int _repeatTimes;
        public BlockingQueue<byte[]> AudioRxQueue { get => _audioRxQ; }
        public BlockingQueue<byte[]> AudioTxQueue { get => _audioTxQ; }
        public BlockingQueue<byte[]> CtrlRxQueue { get => _ctrlRxQ; }
        public BlockingQueue<byte[]> CtrlTxQueue { get => _ctrlTxQ; }
        public bool IsAudioConnected { get => _isAudioConnected; }
        public bool IsCtrlConnected { get => _isCtrlConnected; }
        public int RepeatTimes
        {
            get
            {
                if (_cs != null)
                {
                    return _cs.EachPayloadRepeatTimes;
                }
                else return _repeatTimes;
            }
            set
            {
                if (_cs != null)
                {
                    _cs.EachPayloadRepeatTimes = value;
                }
                _repeatTimes = value;
            }
        }
        public MyProtocolClientSideService(ILogger<MyProtocolClientSideService> logger, ILogger<ClientSide> csLogger, IConfiguration conf, IConfigService ics)
        {
            logger.LogDebug($"MyProtocolClientSideService creating...");
            _logger = logger;
            _csLogger = csLogger;
            _trustedBridgePublicKeys = new List<ECPublicKeyParameters>();
            _openEvent = new AutoResetEvent(false);
            _closeEvent = new AutoResetEvent(false);
            _openedEvent = new AutoResetEvent(false);
            _closedEvent = new AutoResetEvent(false);
            _exitEvent = new AutoResetEvent(false);
            _audioRxQ = new BlockingQueue<byte[]>();
            _audioTxQ = new BlockingQueue<byte[]>();
            _ctrlRxQ = new BlockingQueue<byte[]>();
            _ctrlTxQ = new BlockingQueue<byte[]>();
            IConfigurationSection pubKx = conf.GetRequiredSection("MyProtocol:ClientSide:IdAuthParam:PublicKey:X");
            IConfigurationSection pubKy = conf.GetRequiredSection("MyProtocol:ClientSide:IdAuthParam:PublicKey:Y");
            IConfigurationSection priK = conf.GetRequiredSection("MyProtocol:ClientSide:IdAuthParam:PrivateKey");
            IConfigurationSection clientPubKs = conf.GetRequiredSection("MyProtocol:ClientSide:TrustedBridgePublicKeys");
            IConfigurationSection bep = conf.GetRequiredSection("MyProtocol:ClientSide:BridgeEndPoint");
            IConfigurationSection rt = conf.GetRequiredSection("MyProtocol:RepeatTimes");
            if (rt.Value != null && int.TryParse(rt.Value, out int irt))
            {
                _repeatTimes = irt;
            }
            else
            {
                _repeatTimes = 3;
            }
            if (bep.Value != null && IPEndPoint.TryParse(bep.Value, out IPEndPoint? result))
            {
                _ipep = result;
            }
            else
            {
                _ipep = new IPEndPoint(0x0100007f, 65233);
            }

            var list = clientPubKs.GetChildren();
            if (pubKx.Value != null && pubKy.Value != null && priK.Value != null)
            {
                var pubk2 = MyProtocolCryptoHelper.ECPublicKeyFromMyBytes(Convert.FromBase64String(pubKx.Value), Convert.FromBase64String(pubKy.Value));
                var prik2 = MyProtocolCryptoHelper.ECPrivateKeyFromMyBytes(Convert.FromBase64String(priK.Value));
                _idKeyPair = new AsymmetricCipherKeyPair(pubk2, prik2);
            }
            else
            {
                _idKeyPair = MyProtocolCryptoHelper.GenerateFixedECDHKeyPair(234);
                _trustedBridgePublicKeys.Add((ECPublicKeyParameters)MyProtocolCryptoHelper.GenerateFixedECDHKeyPair(233).Public);
            }

            foreach (var k in list)
            {
                IConfigurationSection xs = k.GetRequiredSection("X");
                IConfigurationSection ys = k.GetRequiredSection("Y");
                if (xs.Value != null && ys.Value != null)
                {
                    _trustedBridgePublicKeys.Add(MyProtocolCryptoHelper.ECPublicKeyFromMyBytes(Convert.FromBase64String(xs.Value), Convert.FromBase64String(ys.Value)));
                }
            }
            if (ics is ClientConfigService ccs && ccs.ClientMode == ClientModeEnum.Remote)
            {
                _isRemoteMode = true;
            }
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            if (_isRemoteMode)
            {
                int errCount = 0;
                await Task.Run(async () =>
                {
                    try
                    {
                        while (!stoppingToken.IsCancellationRequested)
                        {
                            try
                            {
                                if (WaitHandle.WaitAny([_openEvent, _exitEvent]) != 0)
                                {
                                    break;
                                }
                                _logger.LogDebug($"准备连接桥接器，ip={_ipep}");
                                _cs = new ClientSide(_csLogger, _ipep, _idKeyPair, _trustedBridgePublicKeys, _ctrlRxQ, _audioRxQ, _ctrlTxQ, _audioTxQ, _repeatTimes);
                                _cs.RemoteDevLinkStatusChanged += _cs_RemoteDevLinkStatusChanged;
                                _cs.SessionDroped += _cs_SessionDroped;
                                _cs.Start();
                                await _cs.Auth();
                                DevLinkStatusPayload dlsp = await _cs.GetRemoteDevLinkStatus();
                                _isAudioConnected = dlsp.IsAudioConnected;
                                _isCtrlConnected = dlsp.IsCtrlConnected;
                                _openedEvent.Set();
                                if (WaitHandle.WaitAny([_closeEvent, _exitEvent]) != 0)
                                {
                                    _exitFlag = true;
                                }
                                await _cs.SessionGoodbye();
                                _cs.Stop();
                                _cs.RemoteDevLinkStatusChanged -= _cs_RemoteDevLinkStatusChanged;
                                _cs.SessionDroped -= _cs_SessionDroped;
                                _cs?.SetExitEvent();
                                _closedEvent.Set();
                                if (_exitFlag)
                                {
                                    break;
                                }
                            }
                            catch (OperationCanceledException)
                            {
                                _logger.LogInformation("客户端执行循环内收到操作取消信号，退出");
                                break;
                            }
                            catch (Exception e)
                            {
                                _logger.LogError(e, "客户端执行循环内出现异常");
                                if (errCount < 3)
                                {
                                    if (_openFlag)
                                    {
                                        _openEvent.Set();
                                    }
                                }
                                else
                                {
                                    _logger.LogInformation("客户端执行循环内出现异常大于3次，退出");
                                    _openFlag = false;
                                    _exitEvent.Set();
                                    break;
                                }
                                errCount++;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "客户端执行过程中出现异常");
                    }
                }, stoppingToken);
            }
            else
            {
                _logger.LogInformation("非远程模式运行，退出以节约资源");
            }
        }

        private void _cs_SessionDroped(object? sender, EventArgs e)
        {
            SessionDroped?.Invoke(sender, e);
        }

        private void _cs_RemoteDevLinkStatusChanged(object? sender, RemoteDevLinkStatusChangedEventArgs e)
        {
            switch (e.NoticeType)
            {
                case DevConnectStatusChangedNoticeTypeEnum.CtrlLinkConnected:
                    _isCtrlConnected = true;
                    DevCtrlConnected?.Invoke(this, EventArgs.Empty);
                    break;
                case DevConnectStatusChangedNoticeTypeEnum.CtrlLinkDisconnected:
                    _isCtrlConnected = false;
                    DevCtrlDisconnected?.Invoke(this, EventArgs.Empty);
                    break;
                case DevConnectStatusChangedNoticeTypeEnum.AudioLinkConnected:
                    _isAudioConnected = true;
                    DevAudioConnected?.Invoke(this, EventArgs.Empty);
                    break;
                case DevConnectStatusChangedNoticeTypeEnum.AudioLinkDisconnected:
                    _isAudioConnected = false;
                    DevAudioDisconnected?.Invoke(this, EventArgs.Empty);
                    break;
            }
        }

        public bool Open(IPEndPoint? ipep = null)
        {
            if (_isRemoteMode && (_cs == null || !_cs.RunningStatus))
            {
                if (ipep != null)
                {
                    _ipep = ipep;
                }
                _openFlag = true;
                _openEvent.Set();
                if (WaitHandle.WaitAny([_openedEvent, _exitEvent]) == 0)
                    return true;
            }
            return false;
        }
        public bool Close()
        {
            if (_isRemoteMode && _cs != null && _cs.RunningStatus)
            {
                _openFlag = false;
                _closeEvent.Set();
                if (WaitHandle.WaitAny([_closedEvent, _exitEvent]) == 0)
                    return true;
            }
            return false;
        }
        public async Task<IReadOnlyCollection<BtDevInfo>?> DiscoverMyDevicesTop5()
        {
            if (_cs != null && _cs.RunningStatus)
            {
                return await _cs.DiscoverMyDevicesTop5();
            }
            return null;
        }
        public async Task<bool> ConnectRemoteCtrlChannel(BluetoothAddress? macAddr = null)
        {
            if (_cs != null && _cs.RunningStatus)
            {
                return await _cs.ConnectRemoteCtrlChannel(macAddr?.ToByteArray() ?? []);
            }
            return false;
        }
        public async Task<bool> ConnectRemoteAudioChannel(BluetoothAddress? macAddr = null)
        {
            if (_cs != null && _cs.RunningStatus)
            {
                return await _cs.ConnectRemoteAudioChannel(macAddr?.ToByteArray() ?? []);
            }
            return false;
        }
        public async Task<bool> DisconnectRemoteCtrlChannel()
        {
            if (_cs != null && _cs.RunningStatus)
            {
                return await _cs.DisconnectRemoteCtrlChannel();
            }
            return false;
        }
        public async Task<bool> DisconnectRemoteAudioChannel()
        {
            if (_cs != null && _cs.RunningStatus)
            {
                return await _cs.DisconnectRemoteAudioChannel();
            }
            return false;
        }
        public override Task StartAsync(CancellationToken cancellationToken)
        {
            return base.StartAsync(cancellationToken);
        }
        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _exitEvent.Set();
            return base.StopAsync(cancellationToken);
        }
    }
}
